﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sgml;
using System.Xml.Linq;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading;

namespace SerialManager
{
    /// <summary>
    /// Trida pro nacitani informaci o serialu
    /// </summary>
    class Serial
    {
        #region Interni promenne
        internal static readonly XName XNAnchor = XName.Get( "a" );
        internal static readonly XName XNHref = XName.Get( "href" );
        internal static readonly XName XNTable = XName.Get( "table" );
        internal static readonly XName XNTableRow = XName.Get( "tr" );
        internal static readonly XName XNTableCell = XName.Get( "td" );
        internal static readonly XName XNClass = XName.Get( "class" );

        internal static readonly Regex EpisodeNumberExp
            = new Regex( "([0-9]{1,2})x([0-9]{1,2})", RegexOptions.Singleline | RegexOptions.Compiled );
        #endregion

        /// <summary>
        /// Indikator, zda nacitani informaci skoncilo
        /// </summary>
        public bool Finished { get; private set; }

        /// <summary>
        /// Nazev zpracovavaneho serialu
        /// </summary>
        public string Name { get; private set; }

        /// <summary>
        /// Pocet prectenych sezon
        /// </summary>
        public int SeasonCount { get; private set; }

        /// <summary>
        /// Nacteny seznam informaci o epizodach
        /// </summary>
        public List<EpisodeInfo> Episodes { get; private set; }

        /// <summary>
        /// Status kod vraceny vzdalenym serverem
        /// </summary>
        public HttpStatusCode StatusCode { get; private set; }

        /// <summary>
        /// Pripadna vyjimka zachycena pri nacitani
        /// </summary>
        public Exception Error { get; private set; }

        /// <summary>
        /// Konstrukce tridy se spefikovanym nazvem serialu
        /// </summary>
        /// <param name="name"></param>
        public Serial(string name)
        {
            Name = name;
        }

        private Serial() { }

        /// <summary>
        /// Vytvori a spusti thread, ktery sestavuje soubory ke zpracovani
        /// </summary>
        /// <param name="serialDirectory">Hlavní adresář seriálu</param>
        /// <param name="thread">Vytvořený thread</param>
        /// <returns>Instance teto tridy</returns>
        public static Serial CreateAndStartThread(string serialName, out Thread thread)
        {
            var serial = new Serial( serialName );
            thread = new Thread( new ThreadStart( serial.LoadEpisodeList ) );
            thread.IsBackground = true;
            thread.Start();
            return serial;
        }

        /// <summary>
        /// Metoda threadu - Nacte informace o epizodach ze vzdaleneho serveru
        /// </summary>
        public void LoadEpisodeList()
        {
            var url = "http://www.tvrage.com/" + Name.Replace( " ", "_" ) + "/episode_list/all";

            Console.WriteLine( "Loading episodes information ... [A]bort" );
            Console.WriteLine( url );

            SgmlReader sgml = null;
            string xml = "";
            int tries = 3;

            while( tries > 0 )
            {
                sgml = new SgmlReader();
                sgml.Href = url;

                try
                {
                    xml = sgml.ReadOuterXml();
                    break;
                }
                catch( WebException wex )
                {
                    if( wex.Status == WebExceptionStatus.Timeout )
                    {
                        tries--;
                        Console.WriteLine();
                        Console.WriteLine("Nepodařilo se získat odpověd ze serveru v časovém limitu. Zbývá pokusů: {0}", tries);
                        continue;
                    }

                    Error = wex;
                    break;
                }
                catch( Exception ex )
                {
                    Error = ex;
                    break;
                }
            }

            #if DEBUG
            if( !String.IsNullOrEmpty( xml ) )
            {
                using( var debug = System.IO.File.CreateText( Environment.CurrentDirectory + "/" + Name + "_debug.xml" ) )
                    debug.Write( xml );
            }
            #endif

            if( Error != null )
            {
                Console.Write( "!" );
                return;
            }

            Console.Write( " " );

            XElement doc = null;
            try
            {
                doc = System.Xml.Linq.XElement.Parse( xml );
            }
            catch( Exception ex )
            {
                Error = ex;
                Console.Write( "!" );
                return;
            }
            
            var content = (
                from td in doc.Descendants( XNTableCell )
                where td.GetAttributeValueOrEmpty( XNClass ) == "editcenterbox"
                select td
            ).First();
            
            var seasonTables = (
                from table in content.Descendants( XNTable )
                where table.GetAttributeValueOrEmpty( XNClass ) == "b"
                select table.Elements( XNTableRow )
            );
            
            Episodes = new List<EpisodeInfo>();
            seasonTables.ToList().ForEach( AddSeason );

            Finished = true;
        }

        private void AddSeason( IEnumerable<XElement> episodes )
        {
            SeasonCount++;
            
            Episodes.AddRange(
                from episode in episodes
                let cells = episode.Elements( XNTableCell )
                let fullnumber = EpisodeNumberExp.Match( cells.ElementAt( 1 ).Value )
                where fullnumber.Success
                select new EpisodeInfo
                {
                    Season = Convert.ToInt32( fullnumber.Groups[ 1 ].Value ),
                    Number = Convert.ToInt32( fullnumber.Groups[ 2 ].Value ),
                    Title = cells.ElementAt( 4 ).Value
                }
            );
        }
    }
}
